#include "MCVector2.h"
#include <MCPoint2D.h>
#include <cmath>
#include <iostream>

MCVector2::MCVector2() :
    x(0),
    y(0)
{
}

MCVector2::MCVector2(double x_, double y_) :
    x(x_),
    y(y_)
{
}

MCVector2::MCVector2(const MCVector2 &vector) :
    x(vector.x),
    y(vector.y)
{
}

MCVector2::MCVector2(const MCPoint2D &point) :
    x(point.x),
    y(point.y)
{
}

double MCVector2::magnitude() const
{
    return std::sqrt(x * x + y * y);
}

MCVector2 MCVector2::normalize() const
{
    double mag = magnitude();

    return mag > 0.0 ? MCVector2(x / mag, y / mag) : MCVector2(0.0, 0.0);
}

double MCVector2::dot(const MCVector2 &other) const
{
    return x * other.x + y * other.y;
}

MCVector2 MCVector2::cross(const MCVector2 &other) const
{
    return MCVector2(y * other.x - x * other.y, x * other.y - y * other.x);
}

double MCVector2::crossProduct(const MCVector2 &other) const
{
    return (x * other.x) - (y * other.y);
}

double MCVector2::angle(const MCVector2 &other) const
{
    double dot_product = dot(other);
    double mag1 = magnitude();
    double mag2 = other.magnitude();
    return std::acos(dot_product / (mag1 * mag2));
}

double MCVector2::clockwiseAngle(const MCVector2 &other) const
{
    MCVector2 currentVec = *this;
    double dot = currentVec.dot(other);
    double det = currentVec.x * other.y - currentVec.y * other.x;

    return std::atan2(det, dot);
}

bool MCVector2::collinear(const MCVector2 &vec)
{
    MCVector2 cross_ = cross(vec);

    return std::abs(cross_.magnitude()) == 0;
}

MCVector2 MCVector2::rotate(const MCVector2 &vec, double angleInRadian)
{
    MCVector2 v = vec;
    v.x = v.x * std::cos(angleInRadian) - v.y * std::sin(angleInRadian);
    v.y = v.x * std::sin(angleInRadian) + v.y * std::cos(angleInRadian);

    return v;
}

MCVector2 MCVector2::operator+(const MCVector2 &other) const
{
    return MCVector2(x + other.x, y + other.y);
}

MCVector2 MCVector2::operator-(const MCVector2 &other) const
{
    return MCVector2(x - other.x, y - other.y);
}

MCVector2 MCVector2::operator*(double scalar) const
{
    return MCVector2(x * scalar, y * scalar);
}

MCVector2 MCVector2::operator/(double scalar) const
{
    return MCVector2(x / scalar, y / scalar);
}

MCVector2 &MCVector2::operator=(const MCPoint2D &other)
{
    MCVector2 copy(other);

    std::swap(x, copy.x);
    std::swap(y, copy.y);

    return *this;
}

bool MCVector2::operator==(const MCVector2 &other) const
{
    return x == other.x && y == other.y;
}

bool MCVector2::operator!=(const MCVector2 &other) const
{
    return !(*this == other);
}

bool MCVector2::operator==(const MCPoint2D &other) const
{
    return x == other.x && y == other.y;
}

bool MCVector2::operator!=(const MCPoint2D &other) const
{
    return !(*this == other);
}
